#pragma warning(disable:4099)
#include <gtkmm.h>
#pragma warning(default:4099)

#include <iostream>
#include <fstream>
#include "Logger.h"

#include "RawImage.h"

#pragma warning(disable:4244 4996)
#include <libraw/libraw.h>
#pragma warning(default:4244 4996)

#include <winsock2.h>

namespace RawProject
{
	RawImage::RawImage()
		: Glib::RefPtr<Gdk::Pixbuf>(), _imageBuf8(0), _imgCopy16(0), _imgLuminance(0), _smallWidth(0), _smallHeight(0)
	{
	}

	RawImage::~RawImage()
	{
	    resetBuffers();
	}

	void RawImage::clear()
	{
	    Glib::RefPtr<Gdk::Pixbuf>::clear();
	    resetBuffers();
	}

	bool RawImage::LoadFromFile(Glib::ustring & fileName, WhiteBalance wbFlag)
	{
		Glib::RefPtr<Gdk::Pixbuf>::clear();

		LibRaw rawProcessor;
		rawProcessor.open_file(fileName.c_str());
		rawProcessor.imgdata.params.output_bps = 16;
		switch(wbFlag)
		{
		case WB_AUTO:
			rawProcessor.imgdata.params.use_auto_wb = 1;
			rawProcessor.imgdata.params.use_camera_wb = 0;
			break;
		case WB_CAMERA:
			rawProcessor.imgdata.params.use_auto_wb = 0;
			rawProcessor.imgdata.params.use_camera_wb = 1;
			break;
		}
		rawProcessor.unpack();
		rawProcessor.dcraw_process();
		libraw_processed_image_t* rawImg = rawProcessor.dcraw_make_mem_image();

		if(!rawImg)
		{
            rawProcessor.dcraw_clear_mem(rawImg);
            rawProcessor.recycle();
		    return false;
		}

        resetBuffers();

        int width = rawImg->width;
        int height = rawImg->height;

        _imgCopy16 = new unsigned short [width * height * 3];
		_imageBuf8  = new unsigned char [width * height * 3];

        _smallWidth = width / 5 + 1;
        _smallHeight = height / 5 + 1;

		_imgLuminance = new double[_smallWidth * _smallHeight];
        unsigned int * countBuffer = new unsigned int[_smallWidth * _smallHeight];

        memset(_imgLuminance, 0, _smallWidth * _smallHeight * sizeof(double));
        memset(countBuffer, 0, _smallWidth * _smallHeight * sizeof(unsigned int));

		union {unsigned char charData[2];unsigned short shortData;} sample;
		double sampledb;

		for(unsigned int i=0; i < (unsigned int)width * height; i++)
		{
		    int r = i / width;
		    int c = i % width;

            sample.charData[0] = rawImg->data[6*i];
            _imageBuf8 [3*i  ] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1] = rawImg->data[6*i+1]) : (sample.charData[1] = rawImg->data[6*i+1] == 255 ? rawImg->data[6*i+1] : rawImg->data[6*i+1] + 1);
            sampledb = 0.299 * (_imgCopy16[3*i] = sample.shortData);

            sample.charData[0] = rawImg->data[6*i+2];
			_imageBuf8 [3*i+1] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1] = rawImg->data[6*i+3]) : (sample.charData[1] = rawImg->data[6*i+3] == 255 ? rawImg->data[6*i+3] : rawImg->data[6*i+3] + 1);
            sampledb = 0.587 * (_imgCopy16[3*i+1] = sample.shortData);

            sample.charData[0] = rawImg->data[6*i+4];
			_imageBuf8 [3*i+2] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1] = rawImg->data[6*i+5]) : (sample.charData[1] = rawImg->data[6*i+5] == 255 ? rawImg->data[6*i+5] : rawImg->data[6*i+5] + 1);
            sampledb = 0.114 * (_imgCopy16[3*i+2] = sample.shortData);

            _imgLuminance[(r / 5) * _smallWidth + (c / 5)] = (countBuffer[(r / 5) * _smallWidth + (c / 5)] * _imgLuminance[(r / 5) * _smallWidth + (c / 5)] + sampledb ) / (++countBuffer[(r / 5) * _smallWidth + (c / 5)]);
		}

        delete[] countBuffer;

		Glib::RefPtr<Gdk::Pixbuf>::operator=(Gdk::Pixbuf::create_from_data(_imageBuf8, Gdk::COLORSPACE_RGB, false, 8, rawImg->width, rawImg->height, rawImg->width * 3));

		rawProcessor.dcraw_clear_mem(rawImg);
		rawProcessor.recycle();

		return true;
	}

    void RawImage::resetBuffers()
    {
		if(_imageBuf8)
		{
			delete[] _imageBuf8;
			_imageBuf8 = 0;
		}
		if(_imgCopy16)
		{
			delete[] _imgCopy16;
			_imgCopy16 = 0;
		}
		if(_imgLuminance)
		{
			delete[] _imgLuminance;
			_imgLuminance = 0;
		}
    }

	void RawImage::getPixelValue(int x, int y, unsigned short &R, unsigned short &G, unsigned short &B) const
	{
		if(*this)
		{
			int width = (*this)->get_width();
			int height = (*this)->get_height();

			if(x <= width && y <= height)
			{
				R = _imageBuf8[(width * y + x) * 3    ];
				G = _imageBuf8[(width * y + x) * 3 + 1];
				B = _imageBuf8[(width * y + x) * 3 + 2];
			}
		}
		else
		{
			R = G = B = 0;
		}
	}


	void RawImage::restoreImage()
	{
	    if(this->_imgCopy16)
	    {
            int width = (*this)->get_width();
            int height = (*this)->get_height();

            Glib::RefPtr<Gdk::Pixbuf>::clear();

            union {unsigned char charData[2];unsigned short shortData;} sample;

            for(int i = 0 ; i < width * height ; i++)
            {
                sample.shortData = this->_imgCopy16[3*i  ];
                this->_imageBuf8[3*i  ] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);

                sample.shortData = this->_imgCopy16[3*i+1];
                this->_imageBuf8[3*i+1] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);

                sample.shortData = this->_imgCopy16[3*i+2];
                this->_imageBuf8[3*i+2] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);
            }

            Glib::RefPtr<Gdk::Pixbuf>::operator=(Gdk::Pixbuf::create_from_data(_imageBuf8, Gdk::COLORSPACE_RGB, false, 8, width, height, width * 3));
	    }
	}
}
